using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Web.UI;
using BSpec.Document;
using BSpec.Specification;
using BSpec.Specs;
using NUnit.Framework;

namespace html_renderer_specs
{
    namespace with_scenario_of
    {
        public abstract class rendering_an_html_document : SpecificationBase<HtmlRenderer>
        {
            protected SpecificationDataSet specification_dataset;

            protected override void setup_scenario()
            {
            }

            protected override HtmlRenderer create_subject()
            {
                return new HtmlRenderer();
            }

            protected void given_a_populated_specification_dataset()
            {
                specification_dataset = SpecificationDataSet.Build(Assembly.GetExecutingAssembly());
            }
        }

        public abstract class rendering_specification_elements : rendering_an_html_document
        {
            protected StringBuilder builder;
            protected HtmlTextWriter writer;

            protected override void setup_scenario()
            {
                builder = new StringBuilder();
                writer = new HtmlTextWriter(new StringWriter(builder));
            }
        }
    }

    namespace given_a_populated_specification_dataset
    {
        [TestFixture]
        public class when_rendering : with_scenario_of.rendering_an_html_document
        {
            protected override void setup_scenario()
            {
                given_a_populated_specification_dataset();
            }

            protected override void execute_scenario()
            {
                with_scenario_result(() => the_subject.Render(specification_dataset));
            }

            [Test]
            public void should_render_html()
            {
                the_scenario_result<string>().should_start_with("<html>");
                the_scenario_result<string>().should_end_with("</html>");
            }

            [Test]
            public void should_render_page_title()
            {
                the_scenario_result<string>().should_contain("Specifications for BSpec");
            }

            [Test]
            public void should_render_specification_title()
            {
                the_scenario_result<string>().should_contain("BSpec");
            }

            [Test]
            public void should_render_table_of_contents()
            {
                the_scenario_result<string>().should_contain("Table of contents");
            }

            [Test]
            public void should_render_concerns()
            {
                the_scenario_result<string>().should_contain("Html renderer");
            }

            [Test]
            public void should_render_concerns_ordered_by_name()
            {
                the_scenario_result<string>().IndexOf("Document generator")
                    .should_be_less_than(the_scenario_result<string>().IndexOf("Html renderer"));
            }

            [Test]
            public void should_render_scenarios()
            {
                the_scenario_result<string>().should_contain("Rendering an html document");
            }

            [Test]
            public void should_render_contexts()
            {
                the_scenario_result<string>().should_contain("Given a populated specification dataset");
            }

            [Test]
            public void should_render_behaviours()
            {
                the_scenario_result<string>().should_contain("When rendering");
            }

            [Test]
            public void should_render_observations()
            {
                the_scenario_result<string>().should_contain("Should render observations");
            }

            [Test]
            public void should_render_footer_message()
            {
                the_scenario_result<string>().should_contain("Generated by BSpec");
            }
        }
    }

    namespace given_an_html_renderer
    {
        [TestFixture]
        public class when_requesting_the_file_extension : with_scenario_of.rendering_an_html_document
        {
            protected override void execute_scenario()
            {
                with_scenario_result(() => the_subject.FileExtension);
            }

            [Test]
            public void should_return_an_html_extension()
            {
                the_scenario_result<string>().should_equal(".html");
            }
        }

        namespace given_a_list_of_concerns
        {
            [TestFixture]
            public class when_rendering_table_of_contents : with_scenario_of.rendering_specification_elements
            {
                private readonly Concern concern = Concern.Create(typeof(when_rendering_a_concern));

                protected override void execute_scenario()
                {
                    HtmlRenderer.RenderTableOfContents(new List<Concern> { concern }, writer);
                    with_scenario_result(() => builder.ToString());
                }

                [Test]
                public void should_render_concern_links()
                {
                    the_scenario_result<string>().should_contain("<a href=\"#Htmlrenderer\">");
                }
            }
        }

        namespace given_an_empty_list_of_concerns
        {
            [TestFixture]
            public class when_rendering_table_of_contents : with_scenario_of.rendering_specification_elements
            {
                protected override void execute_scenario()
                {
                    HtmlRenderer.RenderTableOfContents(new List<Concern>(), writer);
                    with_scenario_result(() => builder.ToString());
                }

                [Test]
                public void should_not_render_the_table_of_contents()
                {
                    the_scenario_result<string>().should_not_contain("Table of contents");
                }
            }
        }

        [TestFixture]
        public class when_rendering_a_concern : with_scenario_of.rendering_specification_elements
        {
            private readonly Concern concern = Concern.Create(typeof(when_rendering_a_concern));

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderConcerns(new List<Concern> { concern }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_render_the_concern_name_and_contents_number()
            {
                the_scenario_result<string>().should_contain("1. Html renderer specifications");
            }

            [Test]
            public void should_render_anchor_point()
            {
                the_scenario_result<string>().should_contain("<a name=\"Htmlrenderer\"");
            }
        }

        [TestFixture]
        public class when_rendering_a_scenario : with_scenario_of.rendering_specification_elements
        {
            private readonly Scenario scenario = Scenario.Create(typeof(when_rendering_a_scenario));

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderScenarios(new List<Scenario> { scenario }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_render_the_scenario_name()
            {
                the_scenario_result<string>().should_contain("Rendering specification elements");
            }
        }

        [TestFixture]
        public class when_rendering_a_null_scenario : with_scenario_of.rendering_specification_elements
        {
            private readonly Scenario scenario = new NullScenario();

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderScenarios(new List<Scenario> { scenario }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_not_render_the_scenario_element()
            {
                the_scenario_result<string>().should_not_contain("<li>");
            }
        }

        [TestFixture]
        public class when_rendering_an_empty_list_of_scenarios : with_scenario_of.rendering_specification_elements
        {
            protected override void execute_scenario()
            {
                HtmlRenderer.RenderScenarios(new List<Scenario>(), writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_not_render_the_scenario_elements()
            {
                the_scenario_result<string>().should_not_contain("<ul>");
            }
        }

        [TestFixture]
        public class when_rendering_a_context : with_scenario_of.rendering_specification_elements
        {
            private readonly Context context = Context.Create(typeof(when_rendering_a_context));

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderContexts(new List<Context> { context }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_render_the_context_name()
            {
                the_scenario_result<string>().should_contain("Given an html renderer");
            }
        }

        [TestFixture]
        public class when_rendering_a_null_context : with_scenario_of.rendering_specification_elements
        {
            private readonly Context context = new NullContext();

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderContexts(new List<Context> { context }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_not_render_the_context_element()
            {
                the_scenario_result<string>().should_not_contain("<li>");
            }
        }

        [TestFixture]
        public class when_rendering_an_empty_list_of_contexts : with_scenario_of.rendering_specification_elements
        {
            protected override void execute_scenario()
            {
                HtmlRenderer.RenderContexts(new List<Context>(), writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_not_render_the_context_elements()
            {
                the_scenario_result<string>().should_not_contain("<ul>");
            }
        }

        [TestFixture]
        public class when_rendering_a_behaviour : with_scenario_of.rendering_specification_elements
        {
            private readonly Behaviour behaviour = Behaviour.Create(typeof(when_rendering_a_behaviour));

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderBehaviours(new List<Behaviour> { behaviour }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_render_the_behaviour_name()
            {
                the_scenario_result<string>().should_contain("When rendering a behaviour");
            }

            [Test]
            public void should_render_observations()
            {
                the_scenario_result<string>().should_contain("<ul>");
                the_scenario_result<string>().should_contain("</ul>");
            }
        }

        [TestFixture]
        public class when_rendering_an_observation : with_scenario_of.rendering_specification_elements
        {
            private readonly Observation observation = Observation.Create(
                typeof(when_rendering_an_observation).GetMethod("should_render_the_observation_name"));

            protected override void execute_scenario()
            {
                HtmlRenderer.RenderObservations(new List<Observation> { observation }, writer);
                with_scenario_result(() => builder.ToString());
            }

            [Test]
            public void should_render_the_observation_name()
            {
                the_scenario_result<string>().should_contain("Should render the observation name");
            }
        }
    }
}